﻿using UnityEngine;
using System.Collections;
using System;

namespace GameEditor
{
    public class MapEditorPanel : MonoBehaviour
    {
        public  Hashtable MapTable;
        MoveEye2D Eye2d;
        Hashtable EditorConfig;

        GameObject CurSelectBlock;

        GameObject PanelPenG;
        GameObject SelectdDrawPen;
        Hashtable PanelPenTable;

        Plane EditorPanel;

        Hashtable CurEditorLayer;
        Transform[] LayerTs; 
        Transform CurLayerT
        {
            get {return LayerTs[(int)CurEditorLayer[":id"]];}
        }

        bool IsLeftMouseDown;
        Vector3 LeftMouseDownPos;

        enum EditorState
        {

        }
        public void OnLoad(Hashtable mapTable)
        {
            EditorConfig = (Hashtable)SGLisp.Helper.EvalValueFromFile(GameEditor.MapFolder + "Editor/MapEditorConfig.clj", SGLisp.RT.MainContext);
            MapTable = mapTable;
            DrawBorder(MapTable);
            LoadMap(MapTable);
           
            //int eye
            Eye2d = MoveEye2D.CreateEye(transform, (Hashtable)EditorConfig[":move-eye"]);
            //init editor
            //panel
            float width = (float)mapTable[":width"];
            float height = (float)mapTable[":height"];
            EditorPanel = new Plane(Vector3.zero, new Vector3(width, 0, 0), new Vector3(0, height, 0));
            //editorlayer
            if (mapTable.ContainsKey(":layers") && ((object[])mapTable[":layers"]).Length>0)
            {
                CurEditorLayer = (Hashtable)((object[])mapTable[":layers"])[0];
            }
       
            //TODO :light need config
            GameObject lightG = new GameObject("light");
            var light = lightG.AddComponent<Light>();
            light.type = LightType.Directional;
            lightG.transform.rotation = Quaternion.Euler(45, 0, 0);
            light.intensity = 0.4f;
            lightG.transform.parent = transform;
        }

        void DrawBorder(Hashtable mapTable)
        {
            float MapWidth = (float)mapTable[":width"];
            float MapHeight = (float)mapTable[":height"];
            GameObject TopBorder = GameObject.CreatePrimitive(PrimitiveType.Cube);
            GameObject BottomBorder = GameObject.CreatePrimitive(PrimitiveType.Cube);
            GameObject LeftBorder = GameObject.CreatePrimitive(PrimitiveType.Cube);
            GameObject RightBorder = GameObject.CreatePrimitive(PrimitiveType.Cube);
            GameObject borderG = new GameObject("border");
            borderG.transform.parent = transform;
            //set name
            TopBorder.name = "TopBorder";
            BottomBorder.name = "BottomBorder";
            LeftBorder.name = "LeftBorder";
            RightBorder.name = "RightBorder";
            //set parent
            TopBorder.transform.parent = borderG.transform;
            BottomBorder.transform.parent = borderG.transform;
            LeftBorder.transform.parent = borderG.transform;
            RightBorder.transform.parent = borderG.transform;
            //set size
            TopBorder.transform.localScale = new Vector3(MapWidth + 2, 1, 1);
            BottomBorder.transform.localScale = new Vector3(MapWidth + 2, 1, 1);
            LeftBorder.transform.localScale = new Vector3(1, MapHeight + 2, 1);
            RightBorder.transform.localScale = new Vector3(1, MapHeight + 2, 1);
            //set position
            TopBorder.transform.localPosition = new Vector3(MapWidth * 0.5f, MapHeight + 0.5f, 0);
            BottomBorder.transform.localPosition = new Vector3(MapWidth * 0.5f, -0.5f, 0);
            LeftBorder.transform.localPosition = new Vector3(-0.5f, MapHeight * 0.5f, 0);
            RightBorder.transform.localPosition = new Vector3(MapWidth + 0.5f, MapHeight * 0.5f, 0);
        }

        void LoadMap(Hashtable mapTable)
        {
            //InitLayer
            object[] layers = (object[])mapTable[":layers"];
            LayerTs = new Transform[layers.Length];
            for (int i = 0; i < layers.Length; i++)
            {
                Hashtable layerDic = (Hashtable)layers[i];
                LayerTs[i] = LoadLayer(layerDic, transform);
            }
        }

        Transform LoadLayer(Hashtable layerTable, Transform parent)
        {
            string layerName = (string)layerTable[":layer-name"];
            int id = (int)layerTable[":id"];
            GameObject LayerG = new GameObject(layerName + "_" + id.ToString());
            LayerG.transform.parent = transform;
            object[] layerElems = (object[])layerTable[":elems"];
            for (int i = 0; i < layerElems.Length; i++)
            {
                object[] ElemArr = (object[])layerElems[i];
                int blockId = (int)ElemArr[0];
                float PosX = (float)ElemArr[1];
                float PosY = (float)ElemArr[2];
                GameObject ObjPrefab = (GameObject)Resources.Load((string)((Hashtable)MapTableMgr.BlockTable[blockId])[":prefab-path"]);
                GameObject ElemBlock = (GameObject)Instantiate(ObjPrefab);
                ElemBlock.name = "blockElem_"+ blockId.ToString();
                ElemBlock.transform.parent = LayerG.transform;
                ElemBlock.transform.localPosition = new Vector3(PosX, PosY, id);
                ElemBlock.AddComponent<Rigidbody>().isKinematic = true;
            }
            return LayerG.transform;
        }

        public void SelectBlockIndex(int blockIndex)
        {
            if (PanelPenG != null)
            {
                Destroy(PanelPenG);
            }
            if (CurSelectBlock != null)
            {
                Destroy( CurSelectBlock.transform.GetChild(0).gameObject);
                CurSelectBlock = null;
            }
            if (blockIndex == -1)
            {
                return;
            }
            Hashtable blockElem = (Hashtable)MapTableMgr.BlockTable[blockIndex];
            this.PanelPenTable = blockElem;
            string prefabPath = (string)blockElem[":prefab-path"];
            PanelPenG = (GameObject)GameObject.Instantiate(Resources.Load(prefabPath));
            PanelPenG.transform.parent = transform;

            PanelPenG.AddComponent<MapEditorPanellPen>();
        }

        public void Update()
        {
            this.UpdatePenPos();
            //this.UpdateDrawPen();
            this.UpdateSelectBlock();
            this.UpdateSelectDrawPen();    
        }

        public void UpdateSelectBlock()
        {
            if (Input.GetMouseButtonDown(0) && PanelPenG == null)
            {
                Ray mouseRay = this.Eye2d.EyeCamera.ScreenPointToRay(Input.mousePosition);
                RaycastHit hitinfo;
                if (Physics.Raycast(mouseRay, out hitinfo))
                {
                    string hitName = hitinfo.collider.gameObject.name.Split('_')[0];
                    if (hitName == "blockElem" && hitinfo.transform.childCount == 0)
                    {
                        if (CurSelectBlock != null)
                        {
                            Destroy(CurSelectBlock.transform.GetChild(0).gameObject);
                        }
                        GameObject copyG = (GameObject)GameObject.Instantiate(hitinfo.collider.gameObject);
                        copyG.transform.parent = hitinfo.collider.transform;
                        copyG.transform.position = hitinfo.transform.position;
                        copyG.gameObject.renderer.sharedMaterial = new Material(Shader.Find("Custom/WireFrame"));
                        copyG.gameObject.renderer.sharedMaterial.SetColor("_LineColor", new Color(1, 0, 0, 0.5f));
                        copyG.gameObject.renderer.sharedMaterial.SetFloat("_LineWidth", 0.02f);
                        CurSelectBlock = hitinfo.transform.gameObject;
                    }
                }
            }

            if (Input.GetKeyDown(KeyCode.Delete))
            {
                Destroy(CurSelectBlock);
            }
        }

        public void UpdatePenPos()
        {
            if (PanelPenG != null&&!IsLeftMouseDown)
            {
                Ray mouseRay = this.Eye2d.EyeCamera.ScreenPointToRay(Input.mousePosition);
                float hitD = 0;
                if (EditorPanel.Raycast(mouseRay, out hitD))
                {
                    Vector3 hitPos = mouseRay.GetPoint(hitD);
                    hitPos.z = (int)CurEditorLayer[":id"];
                    PanelPenG.GetComponent<MapEditorPanellPen>().SetMousePos( this.GetPanelPos(hitPos, PanelPenTable));
                    PanelPenG.transform.rotation = Quaternion.identity;
                }
            }
        }

        public void UpdateSelectDrawPen()
        {
            if (PanelPenG == null)
            {
                return;
            }
            if (Input.GetMouseButtonDown(0)&&PanelPenG!=null)
            {
                IsLeftMouseDown = true;
                LeftMouseDownPos = PanelPenG.transform.localPosition;
                SelectdDrawPen = new GameObject("SelectdDrawPen");
                SelectdDrawPen.transform.parent = PanelPenG.transform.parent;
                SelectdDrawPen.transform.localPosition = PanelPenG.transform.localPosition;
                PanelPenG.gameObject.SetActive(false);
            }

            if (Input.GetMouseButtonUp(0) && IsLeftMouseDown)
            {
                IsLeftMouseDown = false;
                DestroyImmediate(SelectdDrawPen);
                PanelPenG.gameObject.SetActive(true);
                Vector3 curPos = MousePosToScenePos(Input.mousePosition);
                float width = (float)PanelPenTable[":width"];
                float height = (float)PanelPenTable[":height"];
                EachBlockByPos(LeftMouseDownPos, curPos, width, height, (xpos, ypos) =>
                {
                    GameObject NewObj = (GameObject)GameObject.Instantiate(Resources.Load((string)PanelPenTable[":prefab-path"])); ;
                    NewObj.transform.localPosition =new Vector3(xpos,ypos,0);
                    NewObj.transform.parent = CurLayerT;
                    NewObj.AddComponent<Rigidbody>().isKinematic = true;
                    NewObj.name = "blockElem_" + PanelPenTable[":id"].ToString(); 
                });
            }
            if (IsLeftMouseDown)
            {
                Vector3 origPos =LeftMouseDownPos;
                Vector3 curPos =  MousePosToScenePos(Input.mousePosition);
                float width  = (float)PanelPenTable[":width"];
                float height = (float)PanelPenTable[":height"];
                for (int i=0;i< SelectdDrawPen.transform.childCount;i++)
                {
                    SelectdDrawPen.transform.GetChild(i).gameObject.SetActive(false);
                }

                EachBlockByPos(origPos, curPos, width, height, (xpos, ypos) =>
                {
                    xpos = (float)Math.Round((double)xpos,2);
                    ypos = (float)Math.Round((double)ypos, 2);
                    Transform cacheT = SelectdDrawPen.transform.Find(xpos.ToString() + "_" + ypos.ToString());
                    if (cacheT != null)
                    {
                        cacheT.gameObject.SetActive(true);
                        cacheT.transform.position = new Vector3(xpos,ypos, 0);
                    }
                    else
                    {
                        GameObject NewObj = (GameObject)Instantiate(Resources.Load((string)PanelPenTable[":prefab-path"]));
                        NewObj.name = xpos.ToString() + "_" + ypos.ToString();
                        NewObj.transform.parent = SelectdDrawPen.transform;
                        NewObj.transform.rotation = Quaternion.identity;
                        NewObj.transform.position = new Vector3(xpos,ypos, 0);

                    }
                });
            }
        }

        void EachBlockByPos(Vector3 origPos,Vector3 curPos,float blockWidth,float blockHeight,System.Action<float,float> callfn)
        {
            float subX = curPos.x - origPos.x;
            float subY = curPos.y - origPos.y;
            
          
            Action<Action<float, Action<float, float>>, Action<float, float>> EachX = null;
            Action<float, Action<float, float>> EachY = null;
            if (subX <= 0)
            {
                EachX = (eachY, fn) => { for (float x = origPos.x; x >= curPos.x; x-=blockWidth) { EachY(x, fn); } };
            }
            else
            {
               
                EachX = (eachY, fn) => { for (float x = origPos.x; x <= curPos.x; x += blockWidth) { EachY(x, fn); } };
            }
           

            if (subY <= 0)
            {
                EachY = (nowX, fn) => { for (float y = origPos.y; y >= curPos.y; y -= blockHeight) { fn(nowX, y); } };
            }
            else
            {
                EachY = (nowX, fn) => { for (float y = origPos.y; y <= curPos.y; y += blockHeight) { fn(nowX, y); } };
            }
           
            EachX(EachY, (x, y) => { callfn(x, y); });
        }

        Vector3 MousePosToScenePos(Vector3 mousePos)
        {
            Ray mousrRay = this.Eye2d.EyeCamera.ScreenPointToRay(mousePos);
            float enter;
            EditorPanel.Raycast(mousrRay, out enter);
            return mousrRay.GetPoint(enter);
        }

        public void UpdateDrawPen()
        {
            if (Input.GetMouseButtonDown(0) && PanelPenG != null)
            {
                if (PanelPenG.GetComponent<MapEditorPanellPen>().CollisionInfo == null)
                {
                    Ray mouseRay = this.Eye2d.EyeCamera.ScreenPointToRay(Input.mousePosition);
                    float hitD = 0;
                    EditorPanel.Raycast(mouseRay, out hitD);
                    AddBlockElemToLayer(CurEditorLayer,PanelPenTable);
                    
                }
            }
        }

        void AddBlockElemToLayer(Hashtable layerTable,Hashtable blockElem)
        {
            GameObject NewObj = (GameObject)GameObject.Instantiate(Resources.Load((string)PanelPenTable[":prefab-path"])); ;
            NewObj.transform.localPosition = PanelPenG.transform.localPosition;
            NewObj.transform.parent = CurLayerT;
            NewObj.AddComponent<Rigidbody>().isKinematic = true;
            NewObj.name = "blockElem_"+ blockElem[":id"].ToString();
        }

        public Vector3 GetPanelPos(Vector3 point, Hashtable blockTable)
        {
            float MapWidth = (float)MapTable[":width"];
            float MapHeight = (float)MapTable[":height"];
            float width = (float)blockTable[":width"];
            float height = (float)blockTable[":height"];
            float halfWidth = width * 0.5f;
            float halfHeight = height * 0.5f;

            Vector3 RetPos = point;
            if ((point.x - halfWidth) < 0)
            {
                RetPos.x = 0 + halfWidth;
            }
            else if((point.x + halfWidth) > MapWidth)
            {
                RetPos.x = MapWidth - halfWidth;
            }

            if ((point.y - halfHeight) < 0)
            {
                RetPos.y = 0 + halfHeight;
            }
            else if ((point.y + halfHeight) > MapHeight)
            {
                RetPos.y = MapHeight - halfHeight;
            }
            return RetPos;
        }

        public object[] PanelLayersToMap()
        {
            object[] RetLst = new object[this.LayerTs.Length];
            for (int i=0;i<this.LayerTs.Length;i++)
            {
                Hashtable curLayerDic = new Hashtable();
                curLayerDic[":id"] = i;
                curLayerDic[":layer-name"] = this.LayerTs[i].name.Split('_')[0];
                ArrayList ElemLst = new ArrayList();
                for (int j=0;j<this.LayerTs[i].childCount;j++)
                {
                    Transform CurT = this.LayerTs[i].GetChild(j);
                    int blockId = int.Parse( CurT.name.Split('_')[1]);
                    object[] elemData = new object[] { blockId, CurT.transform.localPosition.x, CurT.transform.localPosition.y};
                    ElemLst.Add(elemData);
                }
                curLayerDic[":elems"] = ElemLst.ToArray();
                RetLst[i] = curLayerDic;
            }
            return RetLst;
        }

       
    }
}